﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TreeView.cs" company="YellowFish">
//   Copyright © YellowFish 2009
// </copyright>
// <summary>
//   Defines the TreeView type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using Diagnostics.Annotations;

namespace MobilePal.Views.TreeView
{
  #region Imported Namespaces


  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Windows.Forms;

  using Dialogs;

  using State;


  #endregion


  /// <summary>
  /// Describes and manipulates the tree view.
  /// </summary>
  [ApplicationView("Tree View")]
  public partial class TreeView: UserControl, IView
  {
    #region Constants and Fields


    private readonly List<MenuItem> _menuItems = new List<MenuItem>();

    [NotNull]
    private ActivityCollection _activities;

    private ActivityCategoryCollection _categories;

    [CanBeNull]
    private CurrentActivitiesTreeNode _currentActivitiesNode;

    [NotNull]
    private TreeViewSettings _settings;


    #endregion


    #region Constructors and Destructors


    /// <summary>
    /// Initializes a new instance of the <see cref="TreeView"/> class.
    /// </summary>
    public TreeView()
    {
      InitializeComponent();

      AttachToMenuItemsEvents(contextMenu.MenuItems);

      while (menu.MenuItems.Count > 0)
      {
        _menuItems.Add(menu.MenuItems[0]);
        menu.MenuItems.RemoveAt(0);
      }

      AttachToMenuItemsEvents(_menuItems);

      UpdateMenus();
    }


    #endregion


    #region Events


    /// <summary>
    /// Occurs when an activity was selected.
    /// </summary>
    public event EventHandler<ActivityEventArgs> ActivitySelected;


    #endregion


    #region Properties


    /// <summary>
    /// Gets the menu.
    /// </summary>
    /// <value>
    /// The menu.
    /// </value>
    public List<MenuItem> MenuItems
    {
      get
      {
        return _menuItems;
      }
    }


    /// <summary>
    /// Gets the selected activity.
    /// </summary>
    /// <value>
    /// The selected activity.
    /// </value>
    public Activity SelectedActivity
    {
      get
      {
        if (activitiesTree.SelectedNode is ActivityTreeNode)
        {
          return ((ActivityTreeNode)activitiesTree.SelectedNode).Activity;
        }

        return null;
      }
    }


    #endregion


    #region Public Methods


    /// <summary>
    /// Gets the settings.
    /// </summary>
    /// <returns>
    /// </returns>
    public ViewSettings GetSettings()
    {
      UpdateSettings();

      return _settings;
    }


    /// <summary>
    /// Initializes the view with the specified activity collection and settings.
    /// </summary>
    /// <param name="activityCollection">
    /// The activity collection.
    /// </param>
    /// <param name="categoryCollection">
    /// The category collection.
    /// </param>
    /// <param name="settings">
    /// The settings.
    /// </param>
    public void Initialize(ActivityCollection activityCollection, ActivityCategoryCollection categoryCollection, 
                           ViewSettings settings)
    {
      _activities = activityCollection;
      _categories = categoryCollection;

      activitiesTree.BeginUpdate();

      activitiesTree.Nodes.Clear();

      _settings = settings as TreeViewSettings ?? new TreeViewSettings();

      activitiesTree.CheckBoxes = _settings.ShowCheckBoxes;

      Initialize();

      activitiesTree.EndUpdate();

      if (activitiesTree.Nodes.Count > 0)
      {
        SetSelectedActivity(activitiesTree.Nodes[0]);
      }
    }


    /// <summary>
    /// Toggles the selected activity.
    /// </summary>
    public void ToggleSelectedActivity()
    {
      TreeNode node = activitiesTree.SelectedNode;

      if (node == null || node == _currentActivitiesNode)
      {
        return;
      }

      Activity activity = ((ActivityTreeNode)node).Activity;

      ToggleActivityInProgress(activity);
    }


    #endregion


    #region Methods


    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.GotFocus"/> event.
    /// </summary>
    /// <param name="e">
    /// An <see cref="T:System.EventArgs"/> that contains the event data.
    /// </param>
    protected override void OnGotFocus(EventArgs e)
    {
      activitiesTree.Focus();

      if (SelectedActivity == null && activitiesTree.Nodes.Count > 0)
      {
        activitiesTree.SelectedNode = activitiesTree.Nodes[0];
      }
      else
      {
        UpdateMenus();
      }
    }


    private static List<TreeNode> GetAllNodes(TreeNode node, bool expandedOnly)
    {
      var nodes = new List<TreeNode>();

      foreach (TreeNode child in node.Nodes)
      {
        nodes.Add(child);

        if (!child.IsExpanded && expandedOnly)
        {
          continue;
        }

        nodes.AddRange(GetAllNodes(child, expandedOnly));
      }

      return nodes;
    }


    private static void ToggleActivityInProgress([NotNull] Activity activity)
    {
      Debug.Assert(activity != null, "activity is null.");

      activity.IsInProgress = !activity.IsInProgress;
    }


    private void ActivitiesTreeAfterCheck(object sender, TreeViewEventArgs e)
    {
      if (e.Action == TreeViewAction.Unknown)
      {
        return;
      }

      if (e.Node is CurrentActivitiesTreeNode)
      {
        var currentActivitiesTreeNode = (CurrentActivitiesTreeNode)e.Node;

        if (currentActivitiesTreeNode.Checked)
        {
          e.Node.Checked = false;
        }
        else
        {
          DialogResult result = MessageBox.Show("Are you sure you want to stop all activities?", "Stop All Activities?", 
                                                MessageBoxButtons.YesNo, MessageBoxIcon.Question, 
                                                MessageBoxDefaultButton.Button2);

          if (result == DialogResult.Yes)
          {
            var nodes = new List<ActivityTreeNode>();

            foreach (ActivityTreeNode node in currentActivitiesTreeNode.Nodes)
            {
              nodes.Add(node);
            }

            foreach (ActivityTreeNode node in nodes)
            {
              node.Activity.ToggleIsInProgress();
            }
          }
        }
      }
      else if (e.Node is ActivityTreeNode)
      {
        var activityTreeNode = (ActivityTreeNode)e.Node;
        activityTreeNode.Activity.ToggleIsInProgress();
      }
    }


    private void ActivitiesTreeAfterSelect(object sender, TreeViewEventArgs e)
    {
      if (ActivitySelected != null)
      {
        Activity activity = (e.Node is ActivityTreeNode) ? ((ActivityTreeNode)e.Node).Activity : null;

        ActivitySelected(this, new ActivityEventArgs(activity));
      }

      UpdateMenus();
    }


    private void ActivitiesTreeKeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyCode == Keys.Enter)
      {
        ToggleSelectedActivity();

        e.Handled = true;
        return;
      }

      if (e.KeyCode == Keys.Up)
      {
        PreviousActivity();

        e.Handled = true;
        return;
      }

      if (e.KeyCode == Keys.Down)
      {
        NextActivity();

        e.Handled = true;
        return;
      }

      TreeNode selectedNode = activitiesTree.SelectedNode;

      if (e.KeyCode == Keys.Left)
      {
        if (selectedNode != null)
        {
          if (selectedNode.Nodes.Count == 0 && selectedNode.Parent != null)
          {
            selectedNode.Parent.Collapse();
          }
          else
          {
            selectedNode.Collapse();
          }
        }

        e.Handled = true;
        return;
      }

      if (e.KeyCode == Keys.Right)
      {
        if (selectedNode != null)
        {
          selectedNode.Expand();
        }

        e.Handled = true;
        return;
      }
    }


    private void AddActivity()
    {
      var dialog = new ActivitiesListDialog(_activities, _categories);
      dialog.ShowCheckBoxes = true;

      DialogResult result = dialog.ShowDialog();

      if (result != DialogResult.OK)
      {
        return;
      }

      TreeNode selectedNode = activitiesTree.SelectedNode;
      TreeNodeCollection collection = selectedNode != null ? selectedNode.Nodes : activitiesTree.Nodes;

      activitiesTree.BeginUpdate();

      foreach (Activity activity in dialog.SelectedItems)
      {
        var settings = new ActivityNodeSettings(activity);
        ActivityTreeNode node = CreateNode(settings);

        collection.Add(node);
      }

      activitiesTree.EndUpdate();

      UpdateSettings();
    }


    private void AttachToMenuItemsEvents(IEnumerable menuItems)
    {
      foreach (MenuItem item in menuItems)
      {
        item.Click += MenuItemClick;

        AttachToMenuItemsEvents(item.MenuItems);
      }
    }


    private ActivityTreeNode CreateNode(ActivityNodeSettings settings)
    {
      var node = new ActivityTreeNode(_activities[settings.ActivityName], settings);

      SetNodeSettings(node);
      return node;
    }


    private void CreateNodes(TreeNodeCollection treeNodeCollection, 
                             IEnumerable<ActivityNodeSettingsBase> nodeSettingsCollection)
    {
      foreach (ActivityNodeSettingsBase settings in nodeSettingsCollection)
      {
        if (settings is CurrentActivitiesNodeSettings)
        {
          var nodeSettings = (CurrentActivitiesNodeSettings)settings;
          var currentActivitiesTreeNode = new CurrentActivitiesTreeNode(_activities, nodeSettings);

          SetCurrentActivitiesNodeSettings(nodeSettings, currentActivitiesTreeNode);

          _currentActivitiesNode = currentActivitiesTreeNode;
          treeNodeCollection.Add(currentActivitiesTreeNode);
        }

        if (settings is ActivityNodeSettings)
        {
          var nodeSettings = (ActivityNodeSettings)settings;

          ActivityTreeNode node = CreateNode(nodeSettings);

          treeNodeCollection.Add(node);

          CreateNodes(node.Nodes, nodeSettings.ChildNodesSettings);
        }
      }
    }


    private List<TreeNode> GetAllNodes(bool expandedOnly)
    {
      var nodes = new List<TreeNode>();

      foreach (TreeNode node in activitiesTree.Nodes)
      {
        nodes.Add(node);

        if (!node.IsExpanded && expandedOnly)
        {
          continue;
        }

        nodes.AddRange(GetAllNodes(node, expandedOnly));
      }

      return nodes;
    }


    private void Initialize()
    {
      // TODO <RNovitsky, 04.07.2009>: Set base settings here
      CreateNodes(activitiesTree.Nodes, _settings.Nodes.ChildNodesSettings);
    }


    private void InsertActivity()
    {
      var dialog = new ActivitiesListDialog(_activities, _categories);
      dialog.ShowCheckBoxes = true;

      DialogResult result = dialog.ShowDialog();

      if (result != DialogResult.OK)
      {
        return;
      }

      int insertIndex = 0;
      TreeNodeCollection collection;
      TreeNode selectedNode = activitiesTree.SelectedNode;

      if (selectedNode != null)
      {
        TreeNode parent = selectedNode.Parent;

        if (parent != null)
        {
          collection = parent.Nodes;
          insertIndex = collection.IndexOf(selectedNode);
        }
        else
        {
          collection = activitiesTree.Nodes;
          insertIndex = collection.IndexOf(selectedNode);
        }
      }
      else
      {
        collection = activitiesTree.Nodes;
      }

      activitiesTree.BeginUpdate();

      foreach (Activity activity in dialog.SelectedItems)
      {
        var settings = new ActivityNodeSettings(activity);
        ActivityTreeNode node = CreateNode(settings);

        collection.Insert(insertIndex, node);
        insertIndex++;
      }

      activitiesTree.EndUpdate();

      UpdateSettings();
    }


    private void MenuItemClick(object sender, EventArgs e)
    {
      var item = (MenuItem)sender;

      if (item.Text == insertActivityMenuItem.Text)
      {
        InsertActivity();
      }

      if (item.Text == addActivityMenuItem.Text)
      {
        AddActivity();
      }

      if (item.Text == removeActivityMenuItem.Text)
      {
        RemoveActivity();
      }

      if (item.Text == moveUpMenuItem.Text)
      {
        MoveActivityUp();
      }

      if (item.Text == moveDownMenuItem.Text)
      {
        MoveActivityDown();
      }

      if(item.Text == expandAllMenuItem.Text)
      {
        activitiesTree.ExpandAll();
      }

      if (item.Text == collapseAllMenuItem.Text)
      {
        activitiesTree.CollapseAll();
      }
    }


    private void MoveActivityDown()
    {
      TreeNode selectedNode = activitiesTree.SelectedNode;
      TreeNode parent = selectedNode.Parent;

      TreeNodeCollection collection = parent != null ? parent.Nodes : activitiesTree.Nodes;

      int index = collection.IndexOf(selectedNode);
      collection.Remove(selectedNode);
      collection.Insert(index + 1, selectedNode);

      UpdateSettings();
    }


    private void MoveActivityUp()
    {
      TreeNode selectedNode = activitiesTree.SelectedNode;
      TreeNode parent = selectedNode.Parent;

      TreeNodeCollection collection = parent != null ? parent.Nodes : activitiesTree.Nodes;

      int index = collection.IndexOf(selectedNode);
      collection.Remove(selectedNode);
      collection.Insert(index - 1, selectedNode);

      UpdateSettings();
    }


    private void NextActivity()
    {
      TreeNode selectedNode = activitiesTree.SelectedNode;

      if (selectedNode != null)
      {
        List<TreeNode> nodes = GetAllNodes(true);

        int indexOf = nodes.IndexOf(selectedNode);
        indexOf++;

        if (indexOf == nodes.Count)
        {
          return;
        }

        SetSelectedActivity(nodes[indexOf]);

        return;
      }
    }


    private void PreviousActivity()
    {
      TreeNode selectedNode = activitiesTree.SelectedNode;

      if (selectedNode != null)
      {
        List<TreeNode> nodes = GetAllNodes(true);

        int indexOf = nodes.IndexOf(selectedNode);
        indexOf--;

        if (indexOf < 0)
        {
          return;
        }

        SetSelectedActivity(nodes[indexOf]);

        return;
      }
    }


    private void RemoveActivity()
    {
      DialogResult result = MessageBox.Show("Are you sure you want to remove selected activity?", "Remove?", 
                                            MessageBoxButtons.YesNo, MessageBoxIcon.Question, 
                                            MessageBoxDefaultButton.Button1);

      if (result != DialogResult.Yes)
      {
        return;
      }

      TreeNode parent = activitiesTree.SelectedNode.Parent;

      if (parent != null)
      {
        parent.Nodes.Remove(activitiesTree.SelectedNode);
      }
      else
      {
        activitiesTree.Nodes.Remove(activitiesTree.SelectedNode);
      }

      UpdateSettings();
    }


    private void SetCurrentActivitiesNodeSettings(CurrentActivitiesNodeSettings nodeSettings, 
                                                  CurrentActivitiesTreeNode node)
    {
      node.LabelFormat = nodeSettings.LabelFormat;
      node.NodesLabelFormat = string.IsNullOrEmpty(nodeSettings.ActivityNodesLabelFormat)
                                ? _settings.InProgressNodesLabelFormat : nodeSettings.ActivityNodesLabelFormat;
    }


    private void SetNodeSettings(ActivityTreeNode node)
    {
      node.LabelFormat = _settings.NodesLabelFormat;
      node.InProgressLabelFormat = _settings.InProgressNodesLabelFormat;
    }


    private void SetSelectedActivity([CanBeNull] TreeNode node)
    {
      activitiesTree.SelectedNode = node;

      if (node != null)
      {
        node.EnsureVisible();
      }
    }


    private void UpdateMenuItems(IEnumerable menuItems)
    {
      foreach (MenuItem item in menuItems)
      {
        bool validNodeSelected = activitiesTree.SelectedNode != null && !(activitiesTree.SelectedNode.Parent is CurrentActivitiesTreeNode);
        TreeNodeCollection collection = null;

        if(validNodeSelected)
        {
          TreeNode parent = activitiesTree.SelectedNode.Parent;
          collection = parent != null ? parent.Nodes : activitiesTree.Nodes;
        }

        if (item.Text == addActivityMenuItem.Text)
        {
          item.Enabled = validNodeSelected;
        }

        if (item.Text == removeActivityMenuItem.Text)
        {
          item.Enabled = validNodeSelected;
        }

        if (item.Text == moveUpMenuItem.Text)
        {
          item.Enabled = validNodeSelected;

          if(validNodeSelected)
          {
            item.Enabled = collection.IndexOf(activitiesTree.SelectedNode) != 0;
          }
        }

        if (item.Text == moveDownMenuItem.Text)
        {
          item.Enabled = validNodeSelected;

          if (validNodeSelected)
          {
            item.Enabled = collection.IndexOf(activitiesTree.SelectedNode) != collection.Count - 1;
          }
        }
      }
    }


    private void UpdateMenus()
    {
      UpdateMenuItems(contextMenu.MenuItems);
      UpdateMenuItems(_menuItems);
    }


    private void UpdateSettings()
    {
      _settings.Nodes.ChildNodesSettings.Clear();

      foreach (IActivityTreeNode node in activitiesTree.Nodes)
      {
        _settings.Nodes.ChildNodesSettings.Add(node.GetSettings());
      }
    }


    #endregion
  }
}